/**
 * An Iterable is something, generally a collection, which can be iterated
 * over, e.g. using a for loop. `Iterable`s produce an `Iterator` which
 * contains the state for a single iteration.
 *
 * Each type can implement Iterable once and declare an associated type,
 * `IteratorT`, which is the type of values that the iterator will produce.
 *
 * A numeric range `(n ... m)` is an intrinsic which compiles to a fast numeric
 * for loop; rewrite rules can be used to optimize iteration over specific
 * collections into iteration over numeric indices to avoid creating
 * unnecessary `Iterator` instances when a collection's type is known.
 */
trait Iterable(IteratorT) {
    public function iterator(): Box[Iterator[IteratorT]];

    // public function all(fn: function (&IteratorT) -> Bool): Bool {
    //     for item in this {
    //         if !fn(item) {
    //             return false;
    //         }
    //     }
    //     return true;
    // }

    // public function any(fn: function (&IteratorT) -> Bool): Bool {
    //     for item in this {
    //         if fn(item) {
    //             return true;
    //         }
    //     }
    //     return false;
    // }

    rules {
        (for $ident in $this {$e}) => {
            var __iterator: Box[Iterator[IteratorT]] = $this.iterator();
            for $ident in __iterator {$e}
        }
    }
}

/**
 * An `Iterator` stores the context necessary for iterating over a collection
 * of values. Each call to `next` should return the next value, or None.
 */
trait Iterator[T] {
    public function next(): (Box[Iterator[T]], Option[T]);

    rules {
        // FIXME: hygiene of macro vars
        (for $ident in $this {$e}) => {
            var __iter: Box[Iterator[T]] = $this;
            var __current: Option[T];
            do {
                (__iter, __current) = __iter.next();
                match __current {
                    Some($ident) => {$e}
                    default => break;
                }
            } while true;
        }
    }
}

struct CountdownIterator[T] {
    public var length: Size;
    public var ptr: Ptr[T];
}

// implement[T] Iterator(T) for CountdownIterator[T] {
//     public function next(): (Box[Iterator], Option[T]) {
//         return (
//             struct CountdownIterator[T] {length: this.length - 1, ptr: &this.ptr[1]},
//             if this.length > 0 then Some(*this.ptr) else None
//         );
//     }
// }

/*
implement Iterable(T) for Iterator[T] {
    public function iterator(): Box[Iterator[T]] {
        return this;
    }
}
*/

// struct CArrayIterator[T, N] {
//     public var index: Uint = 0;
//     public var array: CArray[T, N];
// }

// implement[T, N] Iterable(T) for CArray[T, N] {
//     public function iterator(): Box[Iterator[T]] {
//         return struct CArrayIterator {
//             array: &this,
//         };
//     }
// }

// implement[T, N] Iterator[T] for CArrayIterator[T, N] {
//     public function next(): (Box[Iterator[T]], Option[T]) {
//         if this.index >= this.array.length {
//             return (this, None);
//         } else {
//             var val = this.array[this.index++];
//             return (this, Some(val));
//         }
//     }
// }
